package cn.stimd.spring.transaction.support;

import cn.stimd.spring.transaction.PlatformTransactionManager;
import cn.stimd.spring.transaction.TransactionException;
import cn.stimd.spring.transaction.TransactionStatus;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.UndeclaredThrowableException;

/**
 * Created by Stimd
 * 项目地址：https://gitee.com/stimd/spring-wheel
 * 公众号：Java编程探微
 */
public class TransactionTemplate extends DefaultTransactionDefinition implements TransactionOperations{
    protected final Log logger = LogFactory.getLog(getClass());
    private PlatformTransactionManager transactionManager;

    public TransactionTemplate(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    @Override
    public <T> T execute(TransactionCallback<T> action) throws TransactionException {
        TransactionStatus status = this.transactionManager.getTransaction(this);
        T result;
        try {
            result = action.doInTransaction(status);
        }
        catch (RuntimeException ex) {
            // Transactional code threw application exception -> rollback
            rollbackOnException(status, ex);
            throw ex;
        }
        catch (Error err) {
            // Transactional code threw error -> rollback
            rollbackOnException(status, err);
            throw err;
        }
        catch (Throwable ex) {
            // Transactional code threw unexpected exception -> rollback
            rollbackOnException(status, ex);
            throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
        }
        this.transactionManager.commit(status);
        return result;
    }

    private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
        try {
            this.transactionManager.rollback(status);
        } catch (RuntimeException ex2) {
            logger.error("Application exception overridden by rollback exception", ex);
            throw ex2;
        } catch (Error err) {
            logger.error("Application exception overridden by rollback error", ex);
            throw err;
        }
    }
}
